Micron Document
NexusPi Git Node


Displaying Raw • Download

native/main.cpp HEAD (592c826d) Text, 9.80 KB

T8b949e// Copyright (C) 2026, Chad Attermann
T8b949e//
T8b949e// Portduino owns `main()` (see cores/portduino/main.cpp in the framework).
T8b949e// It defines `portduinoSetup()` as a weak symbol; overriding it here lets
T8b949e// us run config loading + EEPROM init before the Arduino sketch's setup()
T8b949e// is invoked.
T8b949e//
T8b949e// Sequence per Portduino's main:
T8b949e// portduinoCustomInit() -> empty (weak)
T8b949e// argp_parse(...) -> --fsroot etc.
T8b949e// mkdir($fsroot) -> VFS root directory
T8b949e// portduinoVFS->mountpoint(fsroot)
T8b949e// GPIO::init()
T8b949e// portduinoSetup() <- this file
T8b949e// setup() <- Arduino sketch (RNode_Firmware.ino)
T8b949e// while (1) loop();

Tff7b72#Tff7b72include T8b949e"config.h"
Tff7b72#Tff7b72include T8b949e"EEPROMShim.h"
Tff7b72#Tff7b72include T8b949e"TCPHostInterface.h"
Tff7b72#Tff7b72if defined(ENABLE_WEBSOCKETS) && __has_include(<WiFi.h>)
Tff7b72#Tff7b72include T8b949e"../WebSocketConsole.h"
Tff7b72#Tff7b72endif

Tff7b72#Tff7b72include T8b949e<Arduino.h>T8b949e // brings in `extern HardwareSPI SPI;` declaration

Tff7b72#Tff7b72include T8b949e<argp.h>
Tff7b72#Tff7b72include T8b949e<cstdio>
Tff7b72#Tff7b72include T8b949e<cstdlib>
Tff7b72#Tff7b72include T8b949e<cstring>
Tff7b72#Tff7b72include T8b949e<limits.h>T8b949e // PATH_MAX
Tff7b72#Tff7b72include T8b949e<string>
Tff7b72#Tff7b72include T8b949e<unistd.h>
Tff7b72#Tff7b72include T8b949e<sys/stat.h>
Tff7b72#Tff7b72include T8b949e<sys/types.h>

T8b949e// Captured at portduinoSetup() entry, before we chdir() to data_dir.
T8b949e// native/reboot.cpp reads this to restore the launch directory before
T8b949e// execv() so the re-exec'd child can resolve `rnoded.conf` against the
T8b949e// same cwd as the original invocation. Without this, the child runs with
T8b949e// defaults (wrong pin map → radio dead).
Te6edf3stdTff7b72:Tff7b72:Te6edf3string Te6edf3g_launch_cwdTb4b4b4;

T8b949e// strchrnul is a glibc extension that argp-standalone (on macOS, via
T8b949e// Homebrew) expects to find in libc. Apple's libc doesn't provide it,
T8b949e// so we define it here. POSIX-correct equivalent: return a pointer to
T8b949e// the first occurrence of c, or to the trailing NUL if c isn't found.
Tff7b72#Tff7b72ifdef __APPLE__
Tff7b72extern Ta5d6ff"Ta5d6ffCTa5d6ff" Tffa657charTff7b72* Te6edf3strchrnulTb4b4b4(Tff7b72const Tffa657charTff7b72* Te6edf3sTb4b4b4, Tffa657int Te6edf3cTb4b4b4) Tb4b4b4{
Tff7b72while Tb4b4b4(Tff7b72*Te6edf3s Tff7b72&Tff7b72& Tff7b72*Te6edf3s Tff7b72!Tff7b72= Tff7b72static_castTff7b72<Tffa657charTff7b72>Tb4b4b4(Te6edf3cTb4b4b4)Tb4b4b4) Tff7b72+Tff7b72+Te6edf3sTb4b4b4;
Tff7b72return Tff7b72const_castTff7b72<Tffa657charTff7b72*Tff7b72>Tb4b4b4(Te6edf3sTb4b4b4)Tb4b4b4;
Tb4b4b4}
Tff7b72#Tff7b72endif

T8b949e// Boards.h provides EEPROM_SIZE.
Tff7b72#Tff7b72include T8b949e"../Boards.h"

Tff7b72namespace T7ee787native_pinmap Tb4b4b4{
Tffa657void Td2a8ffapplyTb4b4b4(Tb4b4b4)Tb4b4b4;
Tffa657void Td2a8ffbind_linux_gpiosTb4b4b4(Tb4b4b4)Tb4b4b4; T8b949e// no-op on macOS / cross_platform
Tffa657void Td2a8ffseed_eeprom_if_unprovisionedTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// Defined in Utilities.h under the MODEM_RUNTIME branch. The native LoRa
T8b949e// factory in RNode_Firmware.ino setup() reads this to instantiate the
T8b949e// right concrete driver. Must be assigned before setup() runs.
Tff7b72extern Tffa657uint8_t Te6edf3current_modemTb4b4b4;

T8b949e// Forward declarations matching Config.h. We can't include Config.h
T8b949e// directly here because it contains file-scope variable definitions
T8b949e// (e.g. `uint8_t op_mode = MODE_HOST;`) that would cause multiple-
T8b949e// definition link errors when included from a second TU.
Tff7b72extern Tffa657uint8_t Te6edf3op_modeTb4b4b4;
Tff7b72#Tff7b72define FIRMWARE_MODE_TNC 0x12 T8b949e// mirrors MODE_TNC in Config.h

T8b949e// Command-line --config / -c argument storage. Populated by argp before
T8b949e// portduinoSetup() runs; consumed there when choosing the config path.
T8b949e// Precedence: --config flag > $MR_CONFIG env > default "rnoded.conf".
Tff7b72namespace Tb4b4b4{
Tff7b72struct T56d364NativeArgs Tb4b4b4{
Tff7b72const Tffa657charTff7b72* Te6edf3config_path Tff7b72= Tffa657nullptrTb4b4b4;
Tb4b4b4}Tb4b4b4;
Te6edf3NativeArgs Te6edf3g_native_argsTb4b4b4;

Tff7b72struct T56d364argp_option Te6edf3native_optionsTb4b4b4[Tb4b4b4] Tff7b72= Tb4b4b4{
Tb4b4b4{Ta5d6ff"Ta5d6ffconfigTa5d6ff"Tb4b4b4, Ta5d6ff'Ta5d6ffcTa5d6ff'Tb4b4b4, Ta5d6ff"Ta5d6ffPATHTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4, Ta5d6ff"Ta5d6ffPath to rnoded.conf (overrides $MR_CONFIG)Ta5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4}Tb4b4b4,
Tb4b4b4{Tffa657nullptrTb4b4b4, T79c0ff0Tb4b4b4, Tffa657nullptrTb4b4b4, T79c0ff0Tb4b4b4, Tffa657nullptrTb4b4b4, T79c0ff0Tb4b4b4}
Tb4b4b4}Tb4b4b4;

Te6edf3error_t Td2a8ffnative_parse_optTb4b4b4(Tffa657int Te6edf3keyTb4b4b4, Tffa657charTff7b72* Te6edf3argTb4b4b4, Tff7b72struct T56d364argp_stateTff7b72* Te6edf3stateTb4b4b4) Tb4b4b4{
Tff7b72autoTff7b72* Te6edf3args Tff7b72= Tff7b72static_castTff7b72<Te6edf3NativeArgsTff7b72*Tff7b72>Tb4b4b4(Te6edf3stateTff7b72-Tff7b72>Te6edf3inputTb4b4b4)Tb4b4b4;
Tff7b72switch Tb4b4b4(Te6edf3keyTb4b4b4) Tb4b4b4{
Tff7b72case Ta5d6ff'Ta5d6ffcTa5d6ff'Tb4b4b4:
Te6edf3argsTff7b72-Tff7b72>Te6edf3config_path Tff7b72= Te6edf3argTb4b4b4;
Tff7b72return T79c0ff0Tb4b4b4;
Tff7b72case Tff7b72ARGP_KEY_ARGTb4b4b4:
Tff7b72return T79c0ff0Tb4b4b4;
Tff7b72defaultTff7b72:
Tff7b72return Te6edf3ARGP_ERR_UNKNOWNTb4b4b4;
Tb4b4b4}
Tb4b4b4}

Tff7b72struct T56d364argp Te6edf3native_argp Tff7b72= Tb4b4b4{
Te6edf3native_optionsTb4b4b4, Te6edf3native_parse_optTb4b4b4, Tffa657nullptrTb4b4b4, Tffa657nullptrTb4b4b4, Tffa657nullptrTb4b4b4, Tffa657nullptrTb4b4b4, Tffa657nullptr
Tb4b4b4}Tb4b4b4;
Tb4b4b4}

T8b949e// Portduino calls this before argp_parse, so it's the right place to
T8b949e// register our child argp via portduinoAddArguments(). Overrides the
T8b949e// weak default in framework-portduino/cores/portduino/main.cpp.
Tffa657void Te6edf3portduinoCustomInitTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72static Tff7b72struct T56d364argp_child Te6edf3child Tff7b72= Tb4b4b4{
Tff7b72&Te6edf3native_argpTb4b4b4, T79c0ff0Tb4b4b4, Ta5d6ff"Ta5d6ffmicroReticulum daemon optionsTa5d6ff"Tb4b4b4, T79c0ff1
Tb4b4b4}Tb4b4b4;
Te6edf3portduinoAddArgumentsTb4b4b4(Te6edf3childTb4b4b4, Tff7b72&Te6edf3g_native_argsTb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// Portduino calls this before invoking the Arduino sketch's setup().
T8b949e// The symbol replaces Portduino's weak default (which has C++ linkage,
T8b949e// no extern "C") — match its signature exactly so the linker picks ours.
Tffa657void Te6edf3portduinoSetupTb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// 0) Capture the launch directory before any chdir() so the deferred-
T8b949e// reboot path (native/reboot.cpp) can restore it before execv(). The
T8b949e// child resolves `rnoded.conf` (or $MR_CONFIG when relative) against
T8b949e// cwd; without this, after the parent's step 3 chdir() to data_dir
T8b949e// the child can't find the config and silently falls back to
T8b949e// defaults.
Tb4b4b4{
Tffa657char Te6edf3bufTb4b4b4[Te6edf3PATH_MAXTb4b4b4]Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72:Tff7b72:Te6edf3getcwdTb4b4b4(Te6edf3bufTb4b4b4, Tff7b72sizeofTb4b4b4(Te6edf3bufTb4b4b4)Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3g_launch_cwd Tff7b72= Te6edf3bufTb4b4b4;
Tb4b4b4}
Tb4b4b4}

T8b949e// 1) Load config. Precedence: --config CLI flag > $MR_CONFIG env >
T8b949e// default "rnoded.conf". The CLI flag is parsed by argp before
T8b949e// portduinoSetup() runs (registered via portduinoCustomInit()).
Te6edf3stdTff7b72:Tff7b72:Te6edf3string Te6edf3cfg_pathTb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3g_native_argsTb4b4b4.Te6edf3config_path Tff7b72&Tff7b72& Tff7b72*Te6edf3g_native_argsTb4b4b4.Te6edf3config_pathTb4b4b4) Tb4b4b4{
Te6edf3cfg_path Tff7b72= Te6edf3g_native_argsTb4b4b4.Te6edf3config_pathTb4b4b4;
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Tff7b72const Tffa657charTff7b72* Te6edf3cfg_env Tff7b72= Te6edf3stdTff7b72:Tff7b72:Te6edf3getenvTb4b4b4(Ta5d6ff"Ta5d6ffMR_CONFIGTa5d6ff"Tb4b4b4)Tb4b4b4; Te6edf3cfg_env Tff7b72&Tff7b72& Tff7b72*Te6edf3cfg_envTb4b4b4) Tb4b4b4{
Te6edf3cfg_path Tff7b72= Te6edf3cfg_envTb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3cfg_path Tff7b72= Ta5d6ff"Ta5d6ffrnoded.confTa5d6ff"Tb4b4b4;
Tb4b4b4}
Te6edf3native_configTff7b72:Tff7b72:Te6edf3loadTb4b4b4(Te6edf3cfg_pathTb4b4b4)Tb4b4b4;

T8b949e// 2) Optional --data-dir style override via env var.
Tff7b72const Tffa657charTff7b72* Te6edf3data_env Tff7b72= Te6edf3stdTff7b72:Tff7b72:Te6edf3getenvTb4b4b4(Ta5d6ff"Ta5d6ffMR_DATA_DIRTa5d6ff"Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3data_env Tff7b72&Tff7b72& Tff7b72*Te6edf3data_envTb4b4b4) Tb4b4b4{
Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3data_dir Tff7b72= Te6edf3data_envTb4b4b4;
Tb4b4b4}

T8b949e// 3) chdir to data dir so PosixFileSystem (microStore) and the EEPROM
T8b949e// image are scoped there. Portduino's own VFS root (--fsroot) is a
T8b949e// separate concern; we only care about cwd for our own persistence.
Te6edf3mkdirTb4b4b4(Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3data_dirTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3S_IRWXU Tff7b72| Te6edf3S_IRWXG Tff7b72| Te6edf3S_IROTH Tff7b72| Te6edf3S_IXOTHTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3chdirTb4b4b4(Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3data_dirTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72!Tff7b72= T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[portduinoSetup] chdir(%s): %sTffea00\nTa5d6ff"Tb4b4b4,
Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3data_dirTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3stdTff7b72:Tff7b72:Te6edf3strerrorTb4b4b4(Te6edf3errnoTb4b4b4)Tb4b4b4)Tb4b4b4;
T8b949e// Continue anyway — relative paths will resolve to wherever
T8b949e// Portduino started us.
Tb4b4b4}

T8b949e// 4) Banner — make the mode obvious in the log.
Tff7b72#Tff7b72if !defined(LORA_TRANSPORT)
T8b949e// macOS launches headless: no Reticulum interfaces registered
T8b949e// (LORA_TRANSPORT is unflagged from [env:native-macos]).
T8b949e// Future UDPInterface support will land separately.
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[native] no LoRa interface (LORA_TRANSPORT undefined)Tffea00\nTa5d6ff"Tb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72elif defined(PORTDUINO_LINUX_HARDWARE)
T8b949e// Linux native: real /dev/spidev + libgpiod backing.
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[native] Linux hardware mode (spi=%s gpio=%s)Tffea00\nTa5d6ff"Tb4b4b4,
Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3spi_devTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3gpio_chipTb4b4b4.Te6edf3c_strTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72endif

T8b949e// 5) Pin map. apply() copies g_config.pin_* into the extern int pin_*
T8b949e// globals consulted by the modem driver. bind_linux_gpios() then
T8b949e// registers libgpiod-backed pins with Portduino (no-op on macOS).
Te6edf3native_pinmapTff7b72:Tff7b72:Te6edf3applyTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3native_pinmapTff7b72:Tff7b72:Te6edf3bind_linux_gpiosTb4b4b4(Tb4b4b4)Tb4b4b4;

T8b949e// 5a) Surface the configured modem family to setup() so the native LoRa
T8b949e// factory can instantiate the right driver. Must precede setup().
Te6edf3current_modem Tff7b72= Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3modemTb4b4b4;
Tff7b72const Tffa657charTff7b72* Te6edf3modem_nameTb4b4b4;
Tff7b72switch Tb4b4b4(Te6edf3current_modemTb4b4b4) Tb4b4b4{
Tff7b72case T79c0ff0x01Tb4b4b4: Te6edf3modem_name Tff7b72= Ta5d6ff"Ta5d6ffSX1276Ta5d6ff"Tb4b4b4; Tff7b72breakTb4b4b4;
Tff7b72case T79c0ff0x02Tb4b4b4: Te6edf3modem_name Tff7b72= Ta5d6ff"Ta5d6ffSX1278Ta5d6ff"Tb4b4b4; Tff7b72breakTb4b4b4;
Tff7b72case T79c0ff0x03Tb4b4b4: Te6edf3modem_name Tff7b72= Ta5d6ff"Ta5d6ffSX1262Ta5d6ff"Tb4b4b4; Tff7b72breakTb4b4b4;
Tff7b72case T79c0ff0x04Tb4b4b4: Te6edf3modem_name Tff7b72= Ta5d6ff"Ta5d6ffSX1280Ta5d6ff"Tb4b4b4; Tff7b72breakTb4b4b4;
Tff7b72defaultTff7b72: Te6edf3modem_name Tff7b72= Ta5d6ff"Ta5d6ffUNKNOWNTa5d6ff"Tb4b4b4; Tff7b72breakTb4b4b4;
Tb4b4b4}
Te6edf3stdTff7b72:Tff7b72:Te6edf3fprintfTb4b4b4(Te6edf3stderrTb4b4b4, Ta5d6ff"Ta5d6ff[native] modem = %s (0x%02X)Tffea00\nTa5d6ff"Tb4b4b4, Te6edf3modem_nameTb4b4b4, Te6edf3current_modemTb4b4b4)Tb4b4b4;

T8b949e// 5b) KISS-over-TCP host transport. The embedded firmware's USB-serial
T8b949e// KISS channel is replaced on native by a localhost TCP server.
T8b949e// A failure to bind isn't fatal — the daemon keeps running like
T8b949e// an embedded RNode with no USB cable plugged in. kiss_tcp_public
T8b949e// opts into binding on 0.0.0.0; defaults to loopback.
Te6edf3native_kiss_tcpTff7b72:Tff7b72:Te6edf3initTb4b4b4(Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3kiss_tcp_portTb4b4b4,
Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3kiss_tcp_publicTb4b4b4)Tb4b4b4;

Tff7b72#Tff7b72if defined(ENABLE_WEBSOCKETS) && __has_include(<WiFi.h>)
T8b949e// KISS-over-WebSocket on port 8080. Loopback-only by default;
T8b949e// kiss_ws_public=true in rnoded.conf opts into binding 0.0.0.0 the
T8b949e// same way kiss_tcp_public does for the raw KISS TCP server.
Te6edf3ws_consoleTff7b72:Tff7b72:Te6edf3initTb4b4b4(T79c0ff8080Tb4b4b4, Te6edf3native_configTff7b72:Tff7b72:Te6edf3g_configTb4b4b4.Te6edf3kiss_ws_publicTb4b4b4)Tb4b4b4;
Tff7b72#Tff7b72endif

T8b949e// 6) Bind a SimSPIChip as a safety net. With LORA_TRANSPORT removed,
T8b949e// the modem driver no longer initiates SPI activity, but Portduino's
T8b949e// HardwareSPI::transfer() will assert(spiChip) if anything else
T8b949e// (a stray indirect call, a thread, etc.) reaches the SPI subsystem.
T8b949e// SPI.begin() creates a SimSPIChip on cross_platform / non-Linux.
Te6edf3SPITb4b4b4.Te6edf3beginTb4b4b4(Tb4b4b4)Tb4b4b4;

T8b949e// 7) EEPROM image (file-backed shim — see native/EEPROMShim.h).
Te6edf3EEPROMTb4b4b4.Te6edf3beginTb4b4b4(Te6edf3EEPROM_SIZETb4b4b4)Tb4b4b4;
Te6edf3native_pinmapTff7b72:Tff7b72:Te6edf3seed_eeprom_if_unprovisionedTb4b4b4(Tb4b4b4)Tb4b4b4;

T8b949e// 8) Force TNC mode on the native daemon. The embedded firmware flips
T8b949e// op_mode to MODE_TNC only when `hw_ready && eeprom_have_conf()`,
T8b949e// which requires real radio hardware. On the native daemon — where
T8b949e// we may run without a connected modem (or where the modem driver
T8b949e// is fully bypassed on macOS via LORA_TRANSPORT) — we mark the
T8b949e// daemon as TNC explicitly so the Reticulum Transport runs in
T8b949e// enabled mode rather than the host-protocol fallback.
Te6edf3op_mode Tff7b72= Te6edf3FIRMWARE_MODE_TNCTb4b4b4;
Tb4b4b4}

Served by rngit 1.5.2 - Generated in 0.02s